#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule{
    using namespace LockModule;
    using namespace CondModule;

    template<class T>
    class BlockQueue{
    private:
        std::queue<T> _queue;
        Mutex _mutex;
        Cond _producer_cond;
        Cond _consumer_cond;
        int _cap; //容量
        int _consumer_wait_num; //消费者等待数量
        int _producer_wait_num; //生产者等待数量
    protected:
        bool isFull(){
            return _queue.size() == _cap;
        }
        bool isEmpty(){
            return _queue.empty();
        }
    public:
        BlockQueue(int cap = 10):_cap(cap),_consumer_wait_num(0),_producer_wait_num(0){}
        ~BlockQueue(){}

        void EQueue(const T&in)//producer do
        {
            _mutex.Lock();
            while(isFull()){
                std::cout << "queue is full,producer wait" << std::endl;
                _producer_wait_num++;
                _producer_cond.Wait(_mutex);
                _producer_wait_num--;
                std::cout << "producer wake up" << std::endl;
            }
            _queue.push(in);
            //唤醒一个消费者
            if(_consumer_wait_num > 0){
                _consumer_cond.NotifyOne();
            }
            _mutex.UnLock();
        }
        void DQueue(T*out)//consumer do
        {
            _mutex.Lock();
            while(isEmpty()){
                std::cout << "queue is empty,consumer wait" << std::endl;
                _consumer_wait_num++;
                _consumer_cond.Wait(_mutex);
                _consumer_wait_num--;
                std::cout << "consumer wake up" << std::endl;
            }
            *out = _queue.front();
            _queue.pop();
            //唤醒一个生产者
            if(_producer_wait_num > 0){
                _producer_cond.NotifyOne();
            }
            _mutex.UnLock();
        }
    };
}